﻿using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using Bling.WPF;
using Bling.WPF.Custom;
using Bling.Core;
using Bling.Util;
using Bling.Graphics;
using Bling.Matrices;
using Bling.Angles;
using Bling.WPF3D;

namespace Bling.Tutorial {
  public class WPF3DTest : Canvas {
    public WPF3DTest() {
      var Canvas = this.Bl();
      Canvas.Background = Brushes.White;
      QuaternionBl Rotate = this.Property(QuaternionBl.Identity);

      var SphereDiameter = Canvas.Size.Min() - 20d;
      {
        var sphere = new EllipseBl(Canvas) {
          Size = SphereDiameter,
          CenterPosition = Canvas.CenterSize,
          ZIndex = 2,
          Fill = Brushes.LightGray,
          Opacity = .1, IsHitTestVisible = false,
        };
        var outer = new EllipseBl(Canvas) {
          Size = SphereDiameter + 10d,
          CenterPosition = Canvas.CenterSize,
          ZIndex = 1,
          Fill = Brushes.Black,
          Opacity = .1,
        };
        {
          var MouseAt = sphere.Mouse.Position / sphere.Size;
          MouseAt = (MouseAt * 2d - 1d);
          var MouseAt3D = new Double3Bl(MouseAt, (1d - MouseAt.LengthSquared).Max(0d).Sqrt).RawNormalize;

          Rotate[outer.Mouse.Drag.Update] =
            MouseAt3D.AngleBetween(MouseAt3D[outer.Mouse.Drag.In]) * Rotate[outer.Mouse.Drag.In];
        }

      }
      var lbl = new LabelBl(Canvas) {
        RightBottom = Canvas.Size,
        ZIndex = 10,
        Content = "".Bl() + Rotate.ToStringBl(100d),
      };
      var MagnitudeS = new LabelSliderBl(Canvas) {
        RightBottom = lbl.LeftBottom,
        Width = Canvas.Width - lbl.Width,
        ZIndex = 10,
        Minimum = 0, Maximum = 1, Value = .1,
        LabelName = "Magn",
      };
      var FrequencyS = new LabelSliderBl(Canvas) {
        RightBottom = MagnitudeS.RightTop,
        Width = MagnitudeS.Width,
        ZIndex = 10,
        Minimum = 0,
        Maximum = 100,
        Value = 10,
        LabelName = "Freq",
      };

      {
        Double3Bl CameraPosition = new Double3Bl(0, 0, -8 /** (1d / Zoom)*/);
        var Viewport = new Viewport3DBl(Canvas) {
          Size = Canvas.Size.Min(),
          ZIndex = 3,
          CenterPosition = Canvas.CenterSize,
          Camera = new PerspectiveCameraBl() {
            Position = CameraPosition,
            Direction = { Look = new Double3Bl(0, 0, +1), Up = new Double3Bl(0, 1, 0) },
            FieldOfView = .1.PI().Degrees,
          },
          IsHitTestVisible = false,
        };
        new ModelVisual3DBl(Viewport) { // normal ambient light.s
          Content = new AmbientLightBl() { Color = Colors.White, },
        };
        { // just for fun, let's render a parametric surface
          // Create a place holder for the mesh refresh function.
          Action RefreshMesh = () => { };
          // Create two custom dependency properties that trigger mesh refresh when frequency or magnitude slider values change.
          DoubleBl Magnitude = this.Property(MagnitudeS.Value, (newV, oldV) => RefreshMesh());
          DoubleBl Frequency = this.Property(FrequencyS.Value, (newV, oldV) => RefreshMesh());
          Surface sphere // The rendered function is a sphere displaced by an eggcrate pattern.
             = GraphicDefinitions.Sphere.Displace(GraphicDefinitions.EggCrate.Adjust(Frequency, Magnitude, 0));
          MeshGeometry3DBl mesh = new MeshGeometry3DBl();
          // re-assign the mesh refresh function.
          RefreshMesh = mesh.Refresh(sphere[30, 30]);

          ImageBrushBl Brush = new ImageBrushBl() { // skin for the mesh.
            ImageSource = new System.Windows.Media.Imaging.BitmapImage("Resources/Dock.jpg".MakePackUri(typeof(WPF3DTest))),
          };
          new ModelVisual3DBl(Viewport) {
            Transform = new MatrixTransform3DBl() { 
              // ensure mesh rotates with virtual sphere, make mesh smaller.
              Matrix = Matrix4Bl.MakeScale(.8d) * Rotate,
            },
            Content = new GeometryModel3DBl() {
              // render 20 by 20 mesh samples (400 vertices).
              Geometry = mesh,
              Material = new DiffuseMaterialBl() {
                Brush = Brush, 
                Knob = .8d, // 80% directional light absorbed.
                Ambient = { Knob = .2d }, // 20% ambient light absorbed.
              },
            },
          };
        }
        {
          // Throw a cube on the outside of our other mesh
          var cube = new CubeMesh(); 
          // Render each face of the cube as its own mesh so we can assign
          // each face a different color.
          for (int i = 0; i < 6; i++) {
            // Define a directional light in the same direction of the face. 
            new ModelVisual3DBl(Viewport) {
              Content = new DirectionalLightBl() {
                Direction = cube.GetFace(i)[0].Normal,
                Color = i.SelectColor(),
              },
            };
            new ModelVisual3DBl(Viewport) {
              Transform = new MatrixTransform3DBl() {
                Matrix = Matrix4Bl.MakeScale(.8d) * Rotate,
              },
              Content = new GeometryModel3DBl() {
                Geometry = new MeshGeometry3DBl(cube.GetFace(i)),
                Material = new DiffuseMaterialBl() {
                  // Make face color partially transparent.
                  Brush = new SolidColorBrushBl() { Color = i.SelectColor(), Opacity = .3 },
                  Knob = .8d,
                  Ambient = { Knob = .2d },
                },
              },
            };
          }
        }
      }
    }
  }
}